M1 Macで学ぶARMアセンブリプログラミング
#macOS #AppleSilicon #ARM
code:return255.c
#include <stdio.h>
int main() {
return 255;
}
実行してみる
code:sh
$ gcc return255.c -o return255
$ ./return255
$ echo $?
255
C言語からアセンブリソースを生成する
code:sh
$ gcc -S return255.c
code:return255.s
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 12, 0 sdk_version 13, 1
.globl _main ; -- Begin function main
.p2align 2
_main: ; @main
.cfi_startproc
; %bb.0:
sub sp, sp, #16
.cfi_def_cfa_offset 16
str wzr, sp, #12
mov w0, #255
add sp, sp, #16
ret
.cfi_endproc
; -- End function
.subsections_via_symbols
不要そうなコードを削除してみた。
w0 にセットした値が戻り値として使われるっぽい。
( see: https://learn.microsoft.com/ja-jp/cpp/build/arm64-windows-abi-conventions?view=msvc-170#return-values )
code:return255.s
.text
.globl _main
.p2align 2
_main:
; スタック領域を16バイト確保
sub sp, sp, #16
# 0番レジスタの値をスタックに待避
str wzr, sp, #12
# w0 へ即値 255 をセット
mov w0, #255
; スタックを元に戻す
add sp, sp, #16
# 呼び出し元へ戻る
ret
code:fib.c
// hello.c
#include <stdio.h>
int fib(int n) {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
int i = n - 1;
int j = n - 2;
int fi = fib(i);
int fj = fib(j);
return fi + fj;
}
int main() {
int n = 10;
return fib(n);
}
実行してみる
code:sh
$ gcc fib.c -o fib
$ ./fib
$ echo $?
55
code:fib.s
.text
.globl _fib
.p2align 2
_fib:
sub sp, sp, #48
stp x29, x30, sp, #32 ; 16-byte Folded Spill
add x29, sp, #32
stur w0, x29, #-8
ldur w8, x29, #-8
cbnz w8, LBB0_2
b LBB0_1
LBB0_1:
stur wzr, x29, #-4
b LBB0_5
LBB0_2:
ldur w8, x29, #-8
subs w8, w8, #1
b.ne LBB0_4
b LBB0_3
LBB0_3:
mov w8, #1
stur w8, x29, #-4
b LBB0_5
LBB0_4:
ldur w8, x29, #-8
subs w8, w8, #1
stur w8, x29, #-12
ldur w8, x29, #-8
subs w8, w8, #2
str w8, sp, #16
ldur w0, x29, #-12
bl _fib
str w0, sp, #12
ldr w0, sp, #16
bl _fib
str w0, sp, #8
ldr w8, sp, #12
ldr w9, sp, #8
add w8, w8, w9
stur w8, x29, #-4
b LBB0_5
LBB0_5:
ldur w0, x29, #-4
ldp x29, x30, sp, #32 ; 16-byte Folded Reload
add sp, sp, #48
ret
.globl _main
.p2align 2
_main:
; スタック領域確保
sub sp, sp, #32
; x29とx30の値をスタックへ待避
stp x29, x30, sp, #16 ; 16-byte Folded Spill
add x29, sp, #16
stur wzr, x29, #-4
mov w8, #10
str w8, sp, #8
; fib の第一引数は w0 へセットする感じ?
ldr w0, sp, #8
; fib の戻り値は w0 にセットされているっぽい
bl _fib
; スタックへ待避したx29とx30の値を復元
ldp x29, x30, sp, #16 ; 16-byte Folded Reload
; スタックを戻す
add sp, sp, #32
; fib の戻り値 w0 をそのまま返す
ret
登場人物
sp
スタックポインタとして用いるレジスタ
str
ストア命令。レジスタに格納されてる値をメモリに書き込む
stp
二つのレジスタの値をメモリ中の指定したアドレスへ書き込む
stur
メモリ中の指定したアドレスへレジススタの値を書き込む
(strとの違いがよく分からない...)
https://developer.arm.com/documentation/dui0801/g/A64-Data-Transfer-Instructions/STUR
wzr
32ビット幅のゼロレジスタ。読み込むと常に0を返す。64ビット幅のゼロレジスタは xzr で呼び出す
mov
レジスタからレジスタへの値のコピー、即値のロードなどに利用する
w0
32ビット幅の0番レジスタ。64ビット幅の0番レジスタは x0 で呼び出す
ret
return。指定したレジスタのアドレスに無条件分岐する。レジスタを指定しない場合はリンクレジスタ x30 を指定したことになる
bl
関数呼び出し的な?
参考
ARM命令セット
http://www.fos.kuis.kyoto-u.ac.jp/~umatani/le4/arm_spec.html
Linux で Arm64 アセンブリプログラミング (05) ストア命令
https://www.mztn.org/dragon/arm6405str.html
Arm64のレジスタ
https://www.mztn.org/dragon/arm6403reg.html
分岐命令
https://www.mztn.org/dragon/arm6408cond.html